package com.power.poi.excel;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * excel export util
 *
 * @author sunyu on 2018/01/12.
 */
public class ExcelExportUtil {

    private static final String EXCEPTION_MSG = " is unmarked by ExcelAnnotion annotation";
    private static final String NULL_MSG = " The collection is null";
    private static final String DATE_FORMAT = "yyyy-MM-dd";
    private static final String FONT_NAME = "宋体";
    private static final String SHEET_NAME_ERROR = "(This Sheet name is not exist in your specify excel)";
    private static final String HEADER_CONTENT_ERROR = "Header content can not be null";
    private static final String NULL_WORKBOOK = "Excel workbook is null";
    private static final int SHEET_SIZE = 65500;

    /**
     * export map data into excel
     * @param outputStream output stream
     * @param map data of every sheet
     * @param clazz mapping class
     * @return boolean
     */
    public static <T> boolean export(OutputStream outputStream, Map<String, List<T>> map, Class<T> clazz) {
        Workbook workbook;
        if (null == map) {
            throw new NullPointerException(NULL_MSG);
        }
        workbook = new XSSFWorkbook();
        // set style of title
        CellStyle titleStyle = ExcelExportStyle.getTitleStyle(workbook, true);
        // set font style of title
        Font titleFont = ExcelExportFontStyle.getTitleFont(workbook);
        titleStyle.setFont(titleFont);
        // create style of header
        CellStyle headerStyle = ExcelExportStyle.getHeaderStyle(workbook);
        // create style of header font
        Font headerFont = ExcelExportFontStyle.getHeaderFont(workbook);
        headerStyle.setFont(headerFont);
        // get the fields that marked by ExcelAnnotation
        List<Field> fields = ExcelReflectUtil.getFields(clazz);
        if (fields.size() <= 0) {
            throw new RuntimeException(clazz + EXCEPTION_MSG);
        }
        // create body style
        CellStyle bodyStyle = ExcelExportStyle.getBodyStyle(workbook, true);
        for (Map.Entry<String, List<T>> entry : map.entrySet()) {
            // generate Excel sheet
            Sheet sheet = workbook.createSheet(entry.getKey());
            Row row;
            // create area of title
            row = sheet.createRow(0);
            // set content of title
            ExcelPubUtil.setTitle(fields, titleStyle, sheet, row);
            List<T> list = entry.getValue();
            for (int i = 0, len = list.size(); i < len; i++) {
                row = sheet.createRow(i + 1);
                // obtain export object
                T t = list.get(i);
                // set body
                ExcelPubUtil.setBody(fields, t, bodyStyle, row);
            }
        }
        return ExcelPubUtil.getInputStreamIsSuccess(workbook, outputStream);
    }

    /**
     * export excel by a template excel
     *
     * @param inputStream  template input stream
     * @param outputStream output stream
     * @param writeStart   start writing line
     * @param map          data of every sheet
     * @param clazz        mapping class
     * @return boolean
     */
    public static <T> boolean export(InputStream inputStream, OutputStream outputStream, int writeStart,
                                     Map<String, List<T>> map, Class<T> clazz) {
        Workbook workbook = ExcelWorkBook.createWorkbook(inputStream);
        Sheet templateSheet = workbook.getSheetAt(0);
        //error handle
        writeStart = writeStart <= 0 ? 1 : writeStart;
        if (null == map) {
            throw new NullPointerException(NULL_MSG);
        }
        // get the fields that marked by ExcelAnnotation
        List<Field> fields = ExcelReflectUtil.getFields(clazz);
        if (fields.size() <= 0) {
            throw new RuntimeException(clazz + EXCEPTION_MSG);
        }

        Workbook outWorkbook = new XSSFWorkbook();
        // create body style
        CellStyle bodyStyle = ExcelExportStyle.getBodyStyle(outWorkbook, true);
        for (Map.Entry<String, List<T>> entry : map.entrySet()) {
            Sheet sheet = outWorkbook.createSheet(entry.getKey());
            ExcelCopyUtil.copySheet(outWorkbook, templateSheet, sheet, true);
            List<T> list = entry.getValue();
            Row row;
            for (int i = 0, len = list.size(); i < len; i++) {
                row = sheet.createRow(i + writeStart);
                // obtain export object
                T t = list.get(i);
                // set body
                ExcelPubUtil.setBody(fields, t, bodyStyle, row);
            }
        }
        return ExcelPubUtil.getInputStreamIsSuccess(outWorkbook, outputStream);
    }

    /**
     * export list data into excel
     *
     * @param sheetName sheet name
     * @param outputStream   output stream
     * @param list export data
     * @param clazz mapping class
     * @return boolean
     */
    public static <T> boolean export(String sheetName, OutputStream outputStream, List<T> list, Class<T> clazz) {
        Workbook workbook;
        if (null == list) {
            throw new NullPointerException(NULL_MSG);
        }
        workbook = new XSSFWorkbook();
        //set style of title
        CellStyle titleStyle = ExcelExportStyle.getTitleStyle(workbook, true);
        // set font style of title
        Font headerFont = ExcelExportFontStyle.getTitleFont(workbook);
        titleStyle.setFont(headerFont);
        // get the fields that marked by ExcelAnnotation
        List<Field> fields = ExcelReflectUtil.getFields(clazz);
        if (fields.size() <= 0) {
            throw new RuntimeException(clazz + EXCEPTION_MSG);
        }
        // create body style
        CellStyle bodyStyle = ExcelExportStyle.getBodyStyle(workbook, true);
        // set default size of a sheet
        int sheetSize = SHEET_SIZE;

        // Take out a total of how much a sheet
        int sheetNo = ExcelPubUtil.totalSheet(list.size(), sheetSize);

        for (int index = 0; index < sheetNo; index++) {
            // generate Excel sheet
            Sheet sheet = workbook.createSheet();
            // set Excel sheet name
            workbook.setSheetName(index, sheetName + (index + 1));
            Row row;
            // create area of title
            row = sheet.createRow(0);
            // set content of title
            ExcelPubUtil.setTitle(fields, titleStyle, sheet, row);

            int startNo = index * sheetSize;
            int endNo = Math.min(startNo + sheetSize, list.size());
            // Write each record, each record corresponds to a row in the
            // excel
            // table
            for (int i = startNo; i < endNo; i++) {
                row = sheet.createRow(i + 1 - startNo);
                // obtain export object
                T t = list.get(i);
                // set body
                ExcelPubUtil.setBody(fields, t, bodyStyle, row);
            }
        }
        return ExcelPubUtil.getInputStreamIsSuccess(workbook, outputStream);
    }

    /**
     * export data
     * @param sheetName sheet name
     * @param inputStream  template input stream
     * @param outputStream output stream
     * @param writeStart   start writing line
     * @param list         data of list
     * @param clazz        mapping class
     * @return boolean
     */
    public static <T> boolean export(String sheetName, InputStream inputStream, OutputStream outputStream,
                                     int writeStart, List<T> list, Class<T> clazz) {
        Workbook workbook = ExcelWorkBook.createWorkbook(inputStream);
        Sheet templateSheet = workbook.getSheetAt(0);
        //error handle
        writeStart = writeStart <= 0 ? 1 : writeStart;
        if (null == list) {
            throw new NullPointerException(NULL_MSG);
        }
        // get the fields that marked by ExcelAnnotation
        List<Field> fields = ExcelReflectUtil.getFields(clazz);
        if (fields.size() <= 0) {
            throw new RuntimeException(clazz + EXCEPTION_MSG);
        }

        Workbook outWorkbook = new XSSFWorkbook();
        // create body style
        CellStyle bodyStyle = ExcelExportStyle.getBodyStyle(outWorkbook, true);
        // set default size of a sheet
        int sheetSize = SHEET_SIZE;
        // Take out a total of how much a sheet
        int sheetNo = ExcelPubUtil.totalSheet(list.size(), sheetSize);
        for (int index = 0; index < sheetNo; index++) {
            // generate Excel sheet
            Sheet sheet = outWorkbook.createSheet(sheetName + (index + 1));
            ExcelCopyUtil.copySheet(outWorkbook, templateSheet, sheet, true);
            // create area of title
            Row row;
            int startNo = index * sheetSize;
            int endNo = Math.min(startNo + sheetSize, list.size());
            for (int i = startNo; i < endNo; i++) {
                row = sheet.createRow(i + writeStart - startNo);
                // obtain export object
                T t = list.get(i);
                // set body
                ExcelPubUtil.setBody(fields, t, bodyStyle, row);
            }
        }
        return ExcelPubUtil.getInputStreamIsSuccess(outWorkbook, outputStream);
    }
}
